package com.starhub.system.database.service;

import com.starhub.system.database.config.DatabaseUpgradeConfig;
import com.starhub.system.database.dao.SqlExecutionLogDao;
import com.starhub.system.database.entity.SqlExecutionLog;
import com.starhub.system.database.executor.SqlExecutor;
import com.starhub.system.database.manager.SqlFileManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
 * SQL文件处理结果枚举
 */
enum ProcessResult {
    SUCCESS,    // 执行成功
    FAILURE,    // 执行失败
    SKIPPED     // 跳过执行（已成功）
}

/**
 * 数据库升级服务
 */
@Slf4j
@Service
public class DatabaseUpgradeService {
    
    @Autowired
    private DatabaseUpgradeConfig config;
    
    @Autowired
    private SqlFileManager sqlFileManager;
    
    @Autowired
    private SqlExecutor sqlExecutor;
    
    @Autowired
    private SqlExecutionLogDao sqlExecutionLogDao;
    
    /**
     * 执行数据库升级
     */
    public void executeUpgrade() {
        if (!config.isEnabled()) {
            log.info("数据库自动升级功能已禁用");
            return;
        }
        
        log.info("开始执行数据库升级...");
        
        // 验证目录结构
        if (config.isValidateDirectoryStructure() && !sqlFileManager.validateDirectoryStructure()) {
            log.error("目录结构验证失败，无法执行升级");
            return;
        }
        
        // 获取数据库类型
        String databaseType = sqlExecutor.getDatabaseType();
        log.info("当前数据库类型: {}", databaseType);
        
        // 测试数据库连接
        if (!sqlExecutor.testConnection()) {
            log.error("数据库连接失败，无法执行升级");
            return;
        }
        
        // 获取SQL文件列表
        List<File> sqlFiles = sqlFileManager.getSqlFiles(databaseType);
        if (sqlFiles.isEmpty()) {
            log.info("没有找到需要执行的SQL文件");
            return;
        }
        
        log.info("找到 {} 个SQL文件需要处理", sqlFiles.size());
        
        // 统计变量
        int totalFiles = sqlFiles.size();
        int successCount = 0;
        int failureCount = 0;
        int skippedCount = 0;
        
        // 按顺序执行SQL文件
        for (File sqlFile : sqlFiles) {
            ProcessResult result = processSqlFile(databaseType, sqlFile);
            switch (result) {
                case SUCCESS:
                    successCount++;
                    break;
                case FAILURE:
                    failureCount++;
                    break;
                case SKIPPED:
                    skippedCount++;
                    break;
            }
        }
        
        // 输出统计信息
        log.info("数据库升级执行完成 - 总计: {}, 成功: {}, 失败: {}, 跳过: {}", 
                totalFiles, successCount, failureCount, skippedCount);
    }
    
    /**
     * 处理单个SQL文件
     */
    private ProcessResult processSqlFile(String databaseType, File sqlFile) {
        String scriptName = sqlFile.getName();
        log.info("开始处理SQL文件: {}", scriptName);
        
        // 检查是否已有执行记录
        SqlExecutionLog existingLog = sqlExecutionLogDao.findByScriptName(scriptName);
        
        if (existingLog != null) {
            // 已有执行记录
            if (existingLog.getExecutionStatus() == 1) {
                // 之前执行成功，跳过并删除文件
                log.info("SQL文件 {} 之前已执行成功，跳过执行", scriptName);
                if (config.isDeleteAfterSuccess()) {
                    sqlFileManager.deleteSqlFile(sqlFile);
                }
                return ProcessResult.SKIPPED;
            } else {
                // 之前执行失败，重新执行
                log.info("SQL文件 {} 之前执行失败，重新执行", scriptName);
                return executeSqlFile(databaseType, sqlFile, existingLog) ? ProcessResult.SUCCESS : ProcessResult.FAILURE;
            }
        } else {
            // 没有执行记录，直接执行
            log.info("SQL文件 {} 首次执行", scriptName);
            return executeSqlFile(databaseType, sqlFile, null) ? ProcessResult.SUCCESS : ProcessResult.FAILURE;
        }
    }
    
    /**
     * 执行SQL文件
     */
    private boolean executeSqlFile(String databaseType, File sqlFile, SqlExecutionLog existingLog) {
        String scriptName = sqlFile.getName();
        long startTime = System.currentTimeMillis();
        
        try {
            // 读取SQL内容
            String sqlContent = sqlFileManager.readSqlContent(sqlFile);
            if (sqlContent == null) {
                log.error("无法读取SQL文件内容: {}", scriptName);
                recordExecution(databaseType, scriptName, false, "无法读取文件内容", 0L, existingLog);
                return false;
            }
            
            // 执行SQL
            boolean success = sqlExecutor.executeSql(sqlContent, scriptName);
            long duration = System.currentTimeMillis() - startTime;
            
            if (success) {
                // 执行成功
                log.info("【{}】SQL文件执行成功, 耗时: {}ms", scriptName, duration);
                recordExecution(databaseType, scriptName, true, null, duration, existingLog);
                
                // 处理成功后的文件操作
                if (config.isDeleteAfterSuccess()) {
                    sqlFileManager.deleteSqlFile(sqlFile);
                }
                
                if (config.isMoveAfterSuccess()) {
                    sqlFileManager.moveToExecuted(databaseType, sqlFile);
                }
                
                return true;
            } else {
                // 执行失败
                log.error("【{}】SQL文件执行失败, 耗时: {}ms", scriptName, duration);
                recordExecution(databaseType, scriptName, false, "SQL执行失败", duration, existingLog);
                return false;
            }
            
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("【{}】SQL文件执行异常, 耗时: {}ms", scriptName, duration, e);
            recordExecution(databaseType, scriptName, false, e.getMessage(), duration, existingLog);
            return false;
        }
    }
    
    /**
     * 记录执行日志
     */
    private void recordExecution(String databaseType, String scriptName, boolean success, 
                               String errorMessage, Long duration, SqlExecutionLog existingLog) {
        try {
            SqlExecutionLog log = new SqlExecutionLog();
            log.setScriptName(scriptName);
            log.setDatabaseType(databaseType);
            log.setExecutionDate(LocalDate.now());
            log.setExecutionTime(LocalDateTime.now());
            log.setExecutionStatus(success ? 1 : 0);
            log.setErrorMessage(errorMessage);
            log.setExecutionDuration(duration);
            log.setCreateTime(LocalDateTime.now());
            log.setUpdateTime(LocalDateTime.now());
            
            if (existingLog != null) {
                // 更新现有记录
                log.setId(existingLog.getId());
                sqlExecutionLogDao.update(log);
            } else {
                // 插入新记录
                sqlExecutionLogDao.insert(log);
            }
            
        } catch (Exception e) {
            log.error("记录执行日志失败: {}", e.getMessage(), e);
        }
    }
} 